package com.ybox.crawler.process.parse.impl;

import cn.hutool.http.HtmlUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ybox.app.api.domain.AppCollectArticle;
import com.ybox.app.api.service.RemoteAppCollectArticleService;
import com.ybox.common.core.constant.SecurityConstants;
import com.ybox.common.core.text.HMStringUtils;
import com.ybox.common.core.utils.DateUtils;
import com.ybox.common.core.utils.reflect.ReflectUtils;
import com.ybox.common.core.utils.uuid.SnowflakeIdWorker;
import com.ybox.common.security.utils.SecurityUtils;
import com.ybox.crawler.domain.core.label.HtmlLabel;
import com.ybox.common.core.text.ZipUtils;
import com.ybox.crawler.domain.core.parse.impl.CrawlerParseItem;
import com.ybox.crawler.domain.enums.CrawlerEnum;
import com.ybox.crawler.domain.pojos.ClNews;
import com.ybox.crawler.domain.pojos.ClNewsAdditional;
import com.ybox.crawler.domain.pojos.ClNewsComment;
import com.ybox.crawler.process.parse.AbstractHtmlParsePipeline;
import com.ybox.crawler.process.thread.CrawlerThreadPool;
import com.ybox.crawler.service.AppLabelService;
import com.ybox.crawler.service.CrawlerNewsAdditionalService;
import com.ybox.crawler.service.CrawlerNewsCommentService;
import com.ybox.crawler.service.CrawlerNewsService;
import com.ybox.crawler.utils.HtmlParser;
import com.ybox.system.api.model.LoginUser;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


/**
 * 保存阶段
 *
 * @author 16554
 */
@Component("CsdnHtmlParsePipeline")
@Log4j2
public class CrawlerHtmlParsePipeline extends AbstractHtmlParsePipeline<CrawlerParseItem> {

    @Autowired
    private CrawlerNewsAdditionalService crawlerNewsAdditionalService;

    @Autowired
    private CrawlerNewsService crawlerNewsService;

    @Autowired
    private AppLabelService appLabelService;

    @Autowired
    private CrawlerNewsCommentService crawlerNewsCommentService;

    @Autowired
    private RemoteAppCollectArticleService remoteAppCollectArticleService;

    @Value("${crawler.csdn.comment}")
    private String csdnCommentUrl;

    @Value("${crawler.nextupdatehours}")
    private String nextUpdateHours;

    /**
     * 数据处理的入口
     *
     * @param parseItem parseItem
     */
    @Override
    public void handelHtmlData(CrawlerParseItem parseItem) {
        long currentTimeMillis = System.currentTimeMillis();
        log.info("将数据加入线程池进行执行，url:{},handelType:{}", parseItem.getUrl(), parseItem.getHandelType());
        CrawlerThreadPool.submit(() -> {
            // 正向抓取 或者自定义
            if (CrawlerEnum.HandelType.FORWARD.name().equals(parseItem.getHandelType()) || CrawlerEnum.HandelType.CUSTOM.name().equals(parseItem.getHandelType())) {
                log.info("开始处理消息，url:{},handelType:{}", parseItem.getUrl(), parseItem.getHandelType());
                addParseItemMessage(parseItem);
            } else if (CrawlerEnum.HandelType.REVERSE.name().equals(parseItem.getHandelType())) {
                // 更新附加数据
                updateAdditional(parseItem);
            }

            log.info("处理文章数据结束，url:{},handelType:{},耗时:{}", parseItem.getUrl(), parseItem.getHandelType(), System.currentTimeMillis() - currentTimeMillis);
        });
    }

    /**
     * 逆向 更新文章附加信息
     *
     * @param parseItem parseItem
     */
    private void updateAdditional(CrawlerParseItem parseItem) {
        long currentTimeMillis = System.currentTimeMillis();
        log.info("开始更新文章附加数据");
        if (null != parseItem) {
            ClNewsAdditional clNewsAdditional = crawlerNewsAdditionalService.getAdditionalByUrl(parseItem.getUrl());
            if (null != clNewsAdditional) {
                clNewsAdditional.setNewsId(null);
                clNewsAdditional.setUrl(null);
                clNewsAdditional.setReadCount(parseItem.getReadCount());
                clNewsAdditional.setComment(parseItem.getCommentCount());
                clNewsAdditional.setLikes(parseItem.getLikes());
                int nextUpdateHours = getNextUpdateHours(clNewsAdditional.getUpdateNum());
                clNewsAdditional.setNextUpdateTime(DateUtils.addHours(new Date(), nextUpdateHours));
                clNewsAdditional.setUpdateNum(clNewsAdditional.getUpdateNum() + 1);
                crawlerNewsAdditionalService.updateAdditional(clNewsAdditional);
            }
        }
        log.info("更新文章附加数据完成，耗时:{}", System.currentTimeMillis() - currentTimeMillis);
    }

    /**
     * 正向 保存数据发送消息
     *
     * @param parseItem parseItem
     */
    @Transactional
    public void addParseItemMessage(CrawlerParseItem parseItem) {
        long currentTimeMillis = System.currentTimeMillis();
        String url = null;
        String handelTyep = null;
        if (null != parseItem) {
            url = parseItem.getUrl();
            handelTyep = parseItem.getHandelType();
            log.info("开始添加数据，url:{},handelType:{}", url, handelTyep);
            // 添加文章数据
            ClNews clNews = addClNewsData(parseItem);
            // 保存appCollectArticle
            addAppCollectArticle(clNews, parseItem);

            if (null != clNews) {
                // 添加附加信息
                addAdditionalData(parseItem, clNews);
                // 添加评论数据  只有评论数大于0 才添加数据
                if (null != parseItem && null != parseItem.getCommentCount() && parseItem.getCommentCount() > 0) {
                    addCommentData(parseItem, clNews);
                }
                // 发送消息审核文章
                // sendSubmitArticleAutoMessage(clNews.getId());
            }
        }
        log.info("添加数据完成，url:{},handelType:{},耗时:{}", url, handelTyep, System.currentTimeMillis() - currentTimeMillis);
    }

    /**
     * 更新内容
     *
     * @param clNews
     * @param crawlerParseItem
     */
    private void addAppCollectArticle(ClNews clNews, CrawlerParseItem crawlerParseItem) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        AppCollectArticle appCollectArticle = new AppCollectArticle();
        appCollectArticle.setuId(loginUser.getUserid());
        appCollectArticle.setLayout((byte) 2);
        appCollectArticle.setTypeId(107);
        appCollectArticle.setCreateBy(loginUser.getUsername());
        appCollectArticle.setFlag(false);
        appCollectArticle.setStatus(true);
        appCollectArticle.setOrigin(crawlerParseItem.getOrigin());
        appCollectArticle.setOriginUrl(String.valueOf(crawlerParseItem.getUrl()));
        appCollectArticle.setOriginIcon(crawlerParseItem.getIcon());
        appCollectArticle.setTargetUrl(crawlerParseItem.getTargetUrl());
        appCollectArticle.setArticleTitle(clNews.getTitle().replace("\n","").replace("\r","").replace("\t",""));
        appCollectArticle.setArticleContent(HtmlUtil.cleanHtmlTag(crawlerParseItem.getContent()).strip().replace("\n","").replace("\r","").replace("\t",""));
        appCollectArticle.setImages(crawlerParseItem.getImages());
        appCollectArticle.setCreateTime(new Date());
        SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(1,1);
        long l = snowflakeIdWorker.nextId();
        appCollectArticle.setArticleId(String.valueOf(l));
        // TODO 此处容易报错
        remoteAppCollectArticleService.addInner(appCollectArticle, SecurityConstants.INNER);
    }

    // @Autowired
    // private KafkaSender kafkaSender;
    //
    // /**
    //  * 发送消息，审核文章
    //  * @param id
    //  */
    // private void sendSubmitArticleAutoMessage(Integer id) {
    //     log.info("开始发送自动审核文章消息，id:{}",id);
    //     SubmitArticleAuto submitArticleAuto = new SubmitArticleAuto();
    //     submitArticleAuto.setArticleId(id);
    //     submitArticleAuto.setType(SubmitArticleAuto.ArticleType.CRAWLER);
    //     SubmitArticleAuthMessage authMessage = new SubmitArticleAuthMessage();
    //     authMessage.setData(submitArticleAuto);
    //     kafkaSender.sendSubmitArticleAuthMessage(authMessage);
    //     log.info("发送自动审核消息完成，id:{}",id);
    // }


    /**
     * 添加评论数据
     *
     * @param parseItem parseItem
     * @param clNews    clNews
     */
    private void addCommentData(CrawlerParseItem parseItem, ClNews clNews) {
        long currentTimeMillis = System.currentTimeMillis();
        log.info("开始获取文章评论数据");
        List<ClNewsComment> commentList = getCommentData(parseItem);
        if (null != commentList && !commentList.isEmpty()) {
            for (ClNewsComment clNewsComment : commentList) {
                clNewsComment.setNewsId(clNews.getId());
                crawlerNewsCommentService.saveClNewsComment(clNewsComment);
            }
        }
        log.info("获取文章评论数据完成，耗时:{}", System.currentTimeMillis() - currentTimeMillis);
    }

    /**
     * 获取评论列表
     *
     * @param parseItem parseItem
     * @return List<ClNewsComment>
     */
    private List<ClNewsComment> getCommentData(CrawlerParseItem parseItem) {
        // 构建评论的url
        String buildCommentUrl = buildCommentUrl(parseItem);
        // 发送请求，获取数据
        String jsonData = getOriginalRequestJsonData(buildCommentUrl, null);
        // 解析获取的json数据
        List<ClNewsComment> commentList = analysisCommentJsonData(jsonData);
        return commentList;

    }

    /**
     * 解析评论数据
     *
     * @param jsonData jsonData
     * @return List<ClNewsComment>
     */
    private List<ClNewsComment> analysisCommentJsonData(String jsonData) {
        if (StringUtils.isEmpty(jsonData)) {
            return null;
        }
        List<ClNewsComment> commentList = new ArrayList<>();
        JSONObject jsonObject = JSON.parseObject(jsonData);
        Map<String, Object> map = jsonObject.getObject("data", Map.class);
        JSONArray jsonArray = (JSONArray) map.get("list");
        if (null != jsonArray) {
            List<Map> dataInfoList = jsonArray.toJavaList(Map.class);
            for (Map<String, Object> dataInfo : dataInfoList) {
                JSONObject infoObject = (JSONObject) dataInfo.get("info");
                Map<String, Object> infoMap = infoObject.toJavaObject(Map.class);
                ClNewsComment comment = new ClNewsComment();
                comment.setContent(HMStringUtils.toString(infoMap.get("content")));
                comment.setUsername(HMStringUtils.toString(infoMap.get("userName")));
                String articleId = HMStringUtils.toString(infoMap.get("articleId"));
                Date date = DateUtils.parseDate(HMStringUtils.toString(infoMap.get("postTime")));
                comment.setCommentDate(date);
                comment.setCreateTime(new Date());
                comment.setNewsId(Integer.valueOf(articleId));
                commentList.add(comment);
            }
        }
        return commentList;
    }

    /**
     * 生成评论访问连接
     *
     * @param parseItem
     * @return
     */
    private String buildCommentUrl(CrawlerParseItem parseItem) {
        // String buildCommentUrl = csdn_comment_url;
        String buildCommentUrl = csdnCommentUrl;
        Map<String, Object> map = ReflectUtils.beanToMap(parseItem);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            String buildKey = "{" + key + "}";
            Object value = entry.getValue();
            if (null != value) {
                String strValue = value.toString();
                buildCommentUrl = buildCommentUrl.replace(buildKey, strValue);
            }

        }
        return buildCommentUrl;
    }

    /**
     * 保存附加信息
     *
     * @param parseItem parseItem
     * @param clNews    clNews
     */
    private void addAdditionalData(CrawlerParseItem parseItem, ClNews clNews) {
        long currentTimeMillis = System.currentTimeMillis();
        log.info("开始处理文章附件信息数据");
        if (null != parseItem && null != clNews) {
            ClNewsAdditional clNewsAdditional = toClNewsAdditional(parseItem, clNews);
            crawlerNewsAdditionalService.saveAdditional(clNewsAdditional);
        }
        log.info("文章附加数据处理完成，耗时:{}", System.currentTimeMillis() - currentTimeMillis);
    }

    /**
     * 转换数据  additional
     *
     * @param parseItem parseItem
     * @param clNews    clNews
     * @return ClNewsAdditional
     */
    private ClNewsAdditional toClNewsAdditional(CrawlerParseItem parseItem, ClNews clNews) {
        ClNewsAdditional clNewsAdditional = null;
        if (null != parseItem) {
            clNewsAdditional = new ClNewsAdditional();
            // 文章id
            clNewsAdditional.setNewsId(clNews.getId());
            // 阅读数
            clNewsAdditional.setReadCount(parseItem.getReadCount());
            // 回复数
            clNewsAdditional.setComment(parseItem.getCommentCount());
            // 点赞
            clNewsAdditional.setLikes(parseItem.getLikes());
            // url
            clNewsAdditional.setUrl(parseItem.getUrl());
            clNewsAdditional.setUpdateTime(new Date());
            clNewsAdditional.setCreateTime(new Date());
            clNewsAdditional.setUpdateNum(0);
            // 设置下次更新时间
            int nextUpdateHour = getNextUpdateHours(clNewsAdditional.getUpdateNum());
            // 设置下次更新时间
            clNewsAdditional.setNextUpdateTime(DateUtils.addHours(new Date(), nextUpdateHour));

        }
        return clNewsAdditional;
    }


    /**
     * 计算更新次数
     *
     * @param updateNum updateNum
     * @return
     */
    private int getNextUpdateHours(Integer updateNum) {
        if (StringUtils.isNotEmpty(nextUpdateHours)) {
            String[] updateArray = nextUpdateHours.split(",");
            return Integer.parseInt(updateArray[updateNum]);
        } else {
            return 2 << updateNum;
        }
    }


    /**
     * 保存文章信息
     *
     * @param parseItem parseItem
     * @return
     */
    private ClNews addClNewsData(CrawlerParseItem parseItem) {
        log.info("开始添加文章");
        ClNews clNews = null;
        if (null != parseItem) {
            // 内容  content  html文本--》固定的格式
            HtmlParser htmlParser = HtmlParser.getHtmlParser(getParseExpression(), getDefHtmlStyleMap());
            List<HtmlLabel> htmlLabels = htmlParser.parseHtml(parseItem.getContent());
            int type = getDocType(htmlLabels);
            parseItem.setDocType(type);// 图文类型  0 无图 1，单图  2 多图

            String jsonStr = JSON.toJSONString(htmlLabels);
            // 压缩内容
            parseItem.setCompressContent(ZipUtils.gzip(jsonStr));


            // 添加文章
            ClNewsAdditional additionalByUrl = crawlerNewsAdditionalService.getAdditionalByUrl(parseItem.getUrl());
            if (null == additionalByUrl) {
                // 将parseItem转clNews;
                clNews = toClNews(parseItem);
                long currentTimeMillis = System.currentTimeMillis();
                log.info("开始插入新的文章");
                // 保存
                crawlerNewsService.saveNews(clNews);
                log.info("插入新的文章完成，耗时:{}", System.currentTimeMillis() - currentTimeMillis);
            } else {
                log.info("文章url已经存在，不能重复添加，url:{}", additionalByUrl.getUrl());
            }
        }
        log.info("添加文章内容完成");
        return clNews;

    }

    /**
     * parseItem 对象转ClNews
     *
     * @param parseItem parseItem
     * @return
     */
    private ClNews toClNews(CrawlerParseItem parseItem) {
        ClNews clNews = new ClNews();
        clNews.setArticleId(parseItem.getId());
        clNews.setName(parseItem.getAuthor());
        clNews.setLabels(parseItem.getLabels());
        clNews.setContent(parseItem.getCompressContent());
        clNews.setLabelIds(appLabelService.getLabelIds(parseItem.getLabels()));
        clNews.setTitle(parseItem.getTitle());
        clNews.setType(parseItem.getDocType());
        clNews.setStatus((byte) 1);
        clNews.setCreateTime(new Date());
        clNews.setOriginalTime(DateUtils.stringToDate(parseItem.getReleaseDate(), DateUtils.DATE_TIME_FORMAT));
        return clNews;
    }

    /**
     * 获取图文类型
     *
     * @param htmlLabels htmlLabels
     * @return int
     */
    private int getDocType(List<HtmlLabel> htmlLabels) {
        int type = 0;
        int num = 0;
        if (null != htmlLabels && !htmlLabels.isEmpty()) {
            for (HtmlLabel htmlLabel : htmlLabels) {
                if (htmlLabel.getType().equals(CrawlerEnum.HtmlType.IMG_TAG)) {
                    num++;
                }
            }
        }
        if (num == 0) {
            type = 0;
        } else if (num == 1) {
            type = 1;
        } else {
            type = 2;
        }
        return type;
    }

    /**
     * 前置参数处理
     * readCount     阅读数 200
     *
     * @param itemsAll itemsAll
     */
    @Override
    public void preParameterHandel(Map<String, Object> itemsAll) {
        CrawlerEnum.CrawlerType crawlerType = getCrawlerType();
        String releaseDate = HMStringUtils.toString(itemsAll.get("releaseDate"));
        if (crawlerType == CrawlerEnum.CrawlerType.JUEJIN) {
            String readCount = HMStringUtils.toString(itemsAll.get("readCount"));
            // 掘金前置参数处理
            if (StringUtils.isNotEmpty(readCount)) {
                // readCount参数处理
                readCount = readCount.substring(5).strip();
                itemsAll.put("readCount", readCount);
            }
            if (StringUtils.isNotEmpty(releaseDate)) {
                // releaseDate参数处理
                releaseDate = releaseDate.strip().replace("年", "-").replace("月", "-").replace("日", "") + ":00";
            }
        }
        if (crawlerType == CrawlerEnum.CrawlerType.ZHIHU) {
            String commentCount = HMStringUtils.toString(itemsAll.get("commentCount"));
            String likes = HMStringUtils.toString(itemsAll.get("likes"));
            // 知乎前置参数处理
            if (StringUtils.isNotEmpty(commentCount)) {
                // readCount参数处理
                commentCount = commentCount.split(" ")[0];
                itemsAll.put("commentCount", commentCount);
            }
            if (StringUtils.isNotEmpty(likes)) {
                // readCount参数处理
                likes = likes.split(" ")[1];
                itemsAll.put("likes", likes);
            }
            if (StringUtils.isNotEmpty(releaseDate)) {
                // releaseDate参数处理
                releaseDate = releaseDate.replace("编辑于 ", "").strip().replace("年", "-").replace("月", "-").replace("日", "") + ":00";
            }
        } else if (crawlerType == CrawlerEnum.CrawlerType.CSDN) {
            // csdn前置参数处理
            // releaseDate去除“已于”、”修改“ 字样
            if (StringUtils.isNotEmpty(releaseDate)) {
                // releaseDate参数处理
                releaseDate = String.join(" ", Arrays.stream(releaseDate.split(" ")).toList().subList(1, 3));
            }
        }
        itemsAll.put("releaseDate", releaseDate);
    }

    @Override
    public int getPriority() {
        return 1000;
    }
}
